#include "levelpainter.h"
#include "entitydialog.h"
#include <Shinygami/datatree.hpp>
#include <QDebug>

LevelPainter::LevelPainter(QWidget *parent) : QSFMLCanvas(parent), _landscape(*this)
{
    qDebug() << "levelPainter";
    _movingEntity = 0;
}

void LevelPainter::ChangeLandscape(const sf::Texture* tex)
{
  _landscape.SetTexture(*tex);
}

const QString LevelPainter::GenerateName() const
{
    QString        baseName = "new_entity_";
    unsigned short id       = 0;
    QString        merge;

    do
    {
      merge = baseName + QString::number(++id);
    } while (IsNameTaken(merge));
    return (merge);
}

bool LevelPainter::IsNameTaken(const QString &name) const
{
    foreach (LPObject object, _entities)
    {
      if (object.EntityName() == name)
          return (true);
    }
    return (false);
}

void LevelPainter::OnUpdate()
{
  _landscape.Update();

  Draw(_landscape);
  foreach (LPObject entity, _entities)
  {
    entity.Update();
    Draw(entity);
  }
}

bool LevelPainter::AddEntity(QString objectName)
{
  ObjectManager::Object* object  = _objectManager->GetObject(objectName);
  LPObject               entity;

  if (!object)
  {
      qDebug() << "Unable to load object " << objectName;
      return false;
  }
  entity.SetObjectName(object->name);
  entity.SetEntityName(GenerateName());
  qDebug() << "Loading LPObject";
  if (object->textures.count())
  {
      QString            texName = object->textures.first();
      const sf::Texture* texture = _textureManager->GetByName(texName);

      if (texture)
	  entity.SetTexture(*texture);
      else
	  qDebug() << "Error ! No texture named " << texName;
  }
  else if (object->animations.count())
  {
      QString                                animName = object->animations.first();
      const Shiny::SpriteAnimate::Animation* anim     = _animationManager->GetByName(animName);
      Shiny::SpriteAnimate::AnimationArray   array;

      if (anim)
      {
	array.push_back(*anim);
	entity.SetAnimations(array);
      }
      else
	  qDebug() << "Error ! Can't load animation " << animName;
  }

  sf::Vector2f viewSize = GetView().GetSize();
  sf::Vector2f viewPos  = GetView().GetCenter();
  sf::Vector2f pos;

  pos.x = viewPos.x - (viewSize.x / 2);
  pos.y = viewPos.y - (viewSize.y / 2);

  sf::Vector2f halfSize(entity.GetHalfSize());

  entity.SetOrigin(halfSize);
  entity.SetPosition(sf::Vector2f(pos.x + halfSize.x, pos.y + halfSize.y));

  _entities << entity;
  return true;
}

void LevelPainter::mouseDoubleClickEvent(QMouseEvent* event)
{
    LPObject*                 object    = 0;
    QList<LPObject>::iterator current   = _entities.begin();
    QList<LPObject>::iterator end       = _entities.end();
    QPoint                    colPoint  = GetAbsolutePosFromClick(event);
    sf::Vector2f              colVector(colPoint.x(), colPoint.y());

    for (; current != end ; ++current)
    {
        sf::FloatRect rect = (*current).GetGlobalBounds();

        if (rect.Contains(colVector))
          object = &(*current);
    }

    if (object)
    {
        EntityDialog* dialog = new EntityDialog(this);

        connect(dialog, SIGNAL(DeleteEntity(QString)), this, SLOT(RemoveEntity(QString)));
        dialog->SetLPObject(object);
        dialog->open();
    }
}

void LevelPainter::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        _lastMovingPos.x = event->pos().x();
        _lastMovingPos.y = event->pos().y();

        QList<LPObject>::iterator current   = _entities.begin();
        QList<LPObject>::iterator end       = _entities.end();
        QPoint                    colPoint  = GetAbsolutePosFromClick(event);
        sf::Vector2f              colVector(colPoint.x(), colPoint.y());
        int                       entCount  = 0;
        int                       entIt     = 0;

        for (; current != end ; ++current, ++entCount)
        {
            sf::FloatRect rect = (*current).GetGlobalBounds();

            if (!((*current).PositionLocked()) && rect.Contains(colVector))
            {
              _movingEntity = &(*current);
              entIt         = entCount;
            }
        }
        if (_movingEntity)
            _entities.move(entIt, _entities.count() - 1);
    }
    else
      QSFMLCanvas::mousePressEvent(event);
}

void LevelPainter::mouseMoveEvent(QMouseEvent* event)
{
    if (_movingEntity)
    {
        sf::Vector2f newPos(_movingEntity->GetPosition());

        newPos.x -= _lastMovingPos.x - event->pos().x();
        newPos.y -= _lastMovingPos.y - event->pos().y();
        _movingEntity->SetPosition(newPos);
        _lastMovingPos.x = event->pos().x();
        _lastMovingPos.y = event->pos().y();
    }
    QSFMLCanvas::mouseMoveEvent(event);
}

void LevelPainter::mouseReleaseEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
      _movingEntity = 0;
    else
      QSFMLCanvas::mouseReleaseEvent(event);
}

void LevelPainter::Load(const QString &filepath)
{
    DataTree* file = DataTree::Factory::Yaml(filepath.toStdString());

    if (file)
    {
        Data           data(file);
        Data::iterator current = data.begin();
        Data::iterator end     = data.end();

        for (; current != end ; ++current)
        {
            Data type   = (*current)["type"];
            Data pos    = (*current)["position"];
            Data script = (*current)["script"];

            if (!type.Nil() && !pos.Nil())
            {
                Data posX  = pos["x"];
                Data posY  = pos["y"];
                Data angle = pos["angle"];

                if (!posX.Nil() && !posY.Nil())
                {
                    if ((AddEntity(type.Value().c_str())))
                    {
                        LPObject& object   = _entities.last();
                        sf::FloatRect rect = object.GetLocalBounds();

                        object.SetEntityName((*current).Key().c_str());
			object.SetOrigin(rect.Width / 2, rect.Height / 2);
			object.SetPosition(posX, posY);
                        object.Move(sf::Vector2f(rect.Width / 2, rect.Height / 2));
                        if (!angle.Nil())
                            object.SetRotation(angle);
                        if (!script.Nil())
                        {
                            Data scriptSource = script["source"];
                            Data scriptType   = script["type"];

                            if (!scriptSource.Nil() && !scriptType.Nil())
                            {
                                object.SetHasScript(true);
                                object.SetScriptSource(scriptSource.Value().c_str());
                                object.SetScriptType(scriptType.Value().c_str());
                            }
                        }
                    }
                }
                else
                  qDebug() << "Miss position X/Y in entity " << (*current).Key().c_str();
            }
            else
              qDebug() << "Miss type or position in entity " << (*current).Key().c_str();
        }
        delete file;
    }
}

void LevelPainter::Save(const QString &filepath) const
{
    YamlStream stream;

    foreach (LPObject object, _entities)
      object.Save(stream);
    stream.SaveToFile(filepath);
}

void LevelPainter::RemoveEntity(QString name)
{
    QList<LPObject>::iterator current = _entities.begin();
    QList<LPObject>::iterator end     = _entities.end();

    for (; current != end ; ++current)
    {
        if ((*current).EntityName() == name)
        {
            _entities.erase(current);
            break ;
        }
    }
}

/*
  LPObject
  */
void LPObject::Save(YamlStream& stream) const
{
    QPair<QString, std::string> object("type", _objectName.toStdString());
    QPair<QString, float>       posX, posY, rotation;

    posX.first      = "x";
    posY.first      = "y";
    rotation.first  = "angle";
    posX.second     = GetPosition().x - GetOrigin().x;
    posY.second     = GetPosition().y - GetOrigin().y;
    rotation.second = GetRotation();
    stream.newBlock(_entityName);
    stream << object;
      stream.newBlock("position");
      stream << posX << posY;
      if (rotation.second != 0)
        stream << rotation;
      stream.endBlock();
    if (_hasScript)
    {
        QPair<QString, std::string> source("source", _scriptSource.toStdString());
        QPair<QString, std::string> type("type",     _scriptType.toStdString());

        stream.newBlock("script");
        stream << source << type;
        stream.endBlock();
    }
    stream.endBlock();
}

/*
  Landscape
  */
void LevelPainter::Landscape::Update(void)
{
  sf::View     view     = _world.GetView();
  sf::Vector2f position = view.GetCenter();
  sf::Vector2f size     = view.GetSize();

  position.x -= size.x / 2;
  position.y -= size.y / 2;
  SetPosition(position);

  const sf::Texture* tex = GetTexture();
  if (tex)
  {
      sf::Vector2f texSize(tex->GetWidth(), tex->GetHeight());
      sf::Vector2f worldSize(_world.GetMaxSize());
      sf::IntRect  toPrint;

      toPrint.Height = size.y;
      toPrint.Width  = size.x;
      toPrint.Left   = position.x * ((texSize.x - size.x) / (worldSize.x - size.x));
      toPrint.Top    = position.y * ((texSize.y - size.y) / (worldSize.y - size.y));
      SetTextureRect(toPrint);
  }
}
